package org.timemachine.generator;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtField;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.annotation.Annotation;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;

import org.timemachine.generator.spi.AnnoChecker;

public class AnnoCheckerImpl implements AnnoChecker {

	@Override
	public void checkIdAnnotation(CtClass cc, CtField ctField) throws ClassNotFoundException, NotFoundException, CannotCompileException {

		Object annoId = (Object) ctField.getAnnotation(Id.class);
		if (annoId != null) {
			AnnotationsAttribute attr = (AnnotationsAttribute) ctField.getFieldInfo().getAttribute(AnnotationsAttribute.visibleTag);

			Annotation[] annos = attr.getAnnotations();
			List<Annotation> listNew = new ArrayList<Annotation>();
			for (Annotation anno : annos) {
				if (!Id.class.getName().equals(anno.getTypeName()) && !GeneratedValue.class.getName().equals(anno.getTypeName())) {
					listNew.add(anno);
				}		
			}
			if (listNew.size()==0) {
				FieldGeneratorImpl fieldGeneratorImpl = new FieldGeneratorImpl();
				fieldGeneratorImpl.removeGetter(cc, ctField);				
				fieldGeneratorImpl.removeSetter(cc, ctField);
				cc.removeField(ctField);
				
				fieldGeneratorImpl.createFieldWithGetterSetter(cc, ctField);
			} else {
				Object[] objs = listNew.toArray();				
				Annotation[] newAnnos = new Annotation[objs.length];
				int i = 0;
				for (Object obj : objs) {
					newAnnos[i++] = (Annotation) obj;
				}
				attr.setAnnotations(newAnnos);
			}
		}
	}

	@Override
	public void checkOneToManyAnnotation(CtClass cc, CtField ctField) throws ClassNotFoundException, NotFoundException {
		Object annoOneToMany = (Object) ctField.getAnnotation(OneToMany.class);
		if (annoOneToMany != null) {
			FieldGeneratorImpl fieldGeneratorImpl = new FieldGeneratorImpl();
			fieldGeneratorImpl.removeGetter(cc, ctField);				
			fieldGeneratorImpl.removeSetter(cc, ctField);
			cc.removeField(ctField);
		}

	}


	@Override
	public void checkOneToOneAnnotation(CtClass cc, CtField ctField) throws ClassNotFoundException, NotFoundException, CannotCompileException {
		OneToOne annoOneToOne = (OneToOne) ctField.getAnnotation(OneToOne.class);
		if (annoOneToOne != null) {
						
			cc.removeField(ctField);
			if (annoOneToOne.mappedBy() !=null && !annoOneToOne.mappedBy().equals("")) {
				FieldGeneratorImpl fieldGeneratorImpl = new FieldGeneratorImpl();
				fieldGeneratorImpl.removeGetter(cc, ctField);				
				fieldGeneratorImpl.removeSetter(cc, ctField);
			} else {
				CtField newField = new CtField(ctField.getType(), ctField.getName(), cc); 
				cc.addField(newField);			
				AnnotationsAttribute attr = new AnnotationsAttribute(newField.getFieldInfo().getConstPool(), AnnotationsAttribute.visibleTag);
				Annotation idAnn = new Annotation("javax.persistence.ManyToOne", newField.getFieldInfo().getConstPool());
				attr.addAnnotation(idAnn);
				newField.getFieldInfo().addAttribute(attr);
			}
		}

	}

	@Override
	public JoinTable checkManyToManyAnnotation(CtClass cc, CtField ctField) throws ClassNotFoundException, NotFoundException, CannotCompileException, IOException {
		Object annoManyToMany = (Object) ctField.getAnnotation(ManyToMany.class);
		
		if (annoManyToMany != null) {		
			FieldGeneratorImpl fieldGeneratorImpl = new FieldGeneratorImpl();
			fieldGeneratorImpl.removeGetter(cc, ctField);				
			fieldGeneratorImpl.removeSetter(cc, ctField);
			cc.removeField(ctField);
			
			Object annoJoinTable = ctField.getAnnotation(JoinTable.class);
			if (annoJoinTable != null) {				
				return (JoinTable) annoJoinTable;
			}
		}
		return null;
	}

}
